Implicit fork + resume for chat and agent sessions - #155
Draft
big-guy wants to merge 2 commits into
Draft
Conversation
Opening a new agent surface (a json-claude chat tab or an xterm
Claude/Codex tab) now branches off or resumes a prior session implicitly,
driven by what's already open in the worktree:
- A same-kind agent tab already open → fork from the active one (tip).
- No agent tabs of any kind open → resume the worktree's last known
session.
- Otherwise → blank.
Grouping is by agent KIND, not tab surface: chat and xterm Claude share the
~/.claude/projects store, so they fork from / resume each other; Codex tabs
form their own group. Fork uses each agent's native mechanism (Claude
--fork-session, Codex `fork <id>`); the agent mints the new session id and we
bind it to the tab via the existing hook/init discovery path.
Key design: the tab id and the agent session id are decoupled (they were
forced equal). The tab id stays the stable slice/instance/React key; the
on-disk session id lives in TerminalTab.sessionId (persisted, late-bindable
for forks) and JsonClaudeInstance.claudeSessionId. This lets a forked session
own its own transcript without breaking tab identity, and survives reload.
- session-birth.ts: pure, dependency-injected decision logic
(resolveTabBirth / deriveSpawnSpec / agentKindOfTab) + SessionSpawnSpec.
- agents: AgentSpawnOpts.forkFromSessionId + AgentModule.listSessions;
claude/codex buildSpawnArgs emit fork flags.
- json-claude-manager: spawn-spec drives --session-id/--resume/--fork-session;
fork session_id captured from the init event onto the tab.
- index.ts: per-kind birthDeps via getAgent(kind); createChatTab/createAgentTab
+ jsonClaude:addChatTab / agent:addTab IPCs.
- panes-fsm: first-tab resume-last for chat and xterm (bypassed for
prompted/teleport launches).
- renderer: addChatTab/addAgentTab backend methods; transient
forkFromSessionId threaded through XTerminal.
Tests: session-birth (chat+codex birth, cross-surface fork, resume excludes
open ids), json-claude-manager (spawn-spec flags + fork id discovery),
claude/codex buildSpawnArgs fork/resume/blank.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
big-guy
force-pushed
the
session-resume-and-fork
branch
from
June 5, 2026 02:25
b32559e to
964c574
Compare
When an agent tab exits, the restart overlay's button now auto-focuses on the visible pane, so Enter or Space (native button activation) starts a new session without a mouse. Gated on `visible` so hidden tabs' overlays don't steal focus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 5786b28)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Opening a new agent surface — a Chat (json-claude) tab or an xterm Claude/Codex tab — now branches off or resumes a prior session implicitly, driven by what's already open in the worktree:
Grouping is by agent kind, not tab surface: Chat and xterm Claude share
~/.claude/projects, so they fork from / resume each other; Codex tabs form their own group. Fork uses each agent's native mechanism (Claude--fork-session, Codexfork <id>); the agent mints the new session id and we bind it to the tab via the existing hook/init discovery path.Design
The crux is decoupling the tab id from the agent session id (they were forced equal). The tab id stays the stable slice/instance/React key; the on-disk session id lives in
TerminalTab.sessionId(persisted, late-bindable for forks) andJsonClaudeInstance.claudeSessionId. This lets a forked session own its own transcript without breaking tab identity, and survives reload.session-birth.ts— pure, dependency-injected decision logic (resolveTabBirth/deriveSpawnSpec/agentKindOfTab) +SessionSpawnSpec.AgentSpawnOpts.forkFromSessionId+AgentModule.listSessions; Claude/CodexbuildSpawnArgsemit fork flags.--session-id/--resume/--fork-session; the forkedsession_idis captured from the init event onto the tab.birthDepsviagetAgent(kind);createChatTab/createAgentTab+jsonClaude:addChatTab/agent:addTabIPCs.addChatTab/addAgentTabbackend methods; transientforkFromSessionIdthreaded throughXTerminal.Behavior note
Because grouping is cross-surface, a worktree with only an xterm Claude tab open → a new Chat now forks it (previously: blank). Intentional, and matches "fork when another Claude tab is open."
Testing
tsc -b,electron-vite build, and the full vitest suite (1464 tests) pass. New coverage:session-birth(chat+codex birth, cross-surface fork, resume excludes open ids),json-claude-manager(spawn-spec flags + fork id discovery), Claude/CodexbuildSpawnArgsfork/resume/blank.Caveat: Claude
--fork-sessionwas verified end-to-end (new session id minted, fresh self-consistent transcript, source untouched). The Codex fork path is covered by unit tests against the documentedcodex fork <id>syntax but not run live — worth a manual smoke before relying on Codex forking.🤖 Generated with Claude Code